/**
 * @author Jonathan Jung, Timothy Hong, Ronen Adam
 */


import java.io.IOException;
import java.net.*;
import java.util.LinkedList;
import java.util.Queue;

import javax.crypto.Mac;

public class Port extends Thread {
    
    private MacAddress sourceMac; // virtual
    private InetAddress remote; // real
    private short portNum; // real
   
    //Virtual IP used for routing purposes
    private InetAddress vituralIP;
    private int subnetMask;

    //Remote Ip and port
    private MacAddress desinationMac;
    
    private boolean running;// controls state of threads
    private DatagramSocket socket;
    private InetAddress target;
    private int mtu;
    
    private RouterEngine routerEngine;
    private RxEngine receiver;
    private TxEngine transmitter;

    
    
    /**
     * Constructor for Port
     * @param portNum
     * @param virtualIP
     * @param subnetMask
     * @param mtu
     * @param engine
     * @throws UnknownHostException
     * @throws SocketException
     */
    public Port(short portNum, String virtualIP, int subnetMask, int mtu,
            RouterEngine engine) throws UnknownHostException, SocketException {

    	System.out.println("Port construct entry");
    	
    	routerEngine = engine;
        this.portNum = portNum;
        this.vituralIP = InetAddress.getByName(virtualIP);
        this.subnetMask = subnetMask;
        this.mtu = mtu;
        sourceMac = new MacAddress(InetAddress.getLocalHost().getHostAddress(), portNum);
               
        //socket get's conencted to local machine
        socket = new DatagramSocket(portNum, InetAddress.getLocalHost());

        transmitter = new TxEngine(socket, this);
        receiver = new RxEngine(socket, engine);
     //   receiver.start();
        System.out.println("Port construct exit");
    }

    /**
     * Used for command close socket
     */
    public void close() {
        socket.close();
    }

    /**
     * Used for command disconenct socket
     */
    public void disconnect() {
        socket.disconnect();
    }

    /**
     * Connects local port to a remote port *Not synonymous to Router's connect
     * port command
     * 
     * @param targetPort
     * @param targetIP
     * @throws UnknownHostException
     */
    public void connect(int targetPort, String targetIP)
            throws UnknownHostException {
        // connect add <local real port> <remote Real IP:port>
        socket.connect(InetAddress.getByName(targetIP), targetPort);
        desinationMac = new MacAddress(targetIP, (short)targetPort);
        
        //once connect, rx starts to listen
        receiver.start();
    }

    /**
     * Function uses the transmitter engine to send data out
     * 
     * @param out
     * @throws SocketException 
     */
    public void transmit(IPv4Datagram out) throws SocketException {
        transmitter.transmit(out);
    }

    public String getVirtualIP() {
        return vituralIP.getHostAddress();
    }
    
    public short getPortNum()
    {
    	return portNum;
    }
    
    public MacAddress getSrcMac()
    {
    	return sourceMac;
    }
    
    public MacAddress getDesMac()
    {
    	return desinationMac;
    }

    public int getMTU()
    {
        return mtu;
    }
    // function deprecated by threading
    // /**
    // * Function uses the receiver engine to take in data
    // * @throws InstantiationException
    // * @throws IOException
    // */
    // public void receive() throws InstantiationException, IOException
    // {
    // receiver.receive();
    // }

    @Override
    public void run() {
        running = true;

        while (running == true) 
        {
        	
        	
        	try {
				routerEngine.route();
			} catch (InstantiationException | SocketException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
        	
        	//System.out.println(this.currentThread().getName());
        }
    }

    public void halt() {
        running = false;
    }

}